#!/bin/bash

# help
# 查找指定目录下($1)的所有 mach-o 文件中包含指定信息($2)的文件
# 
# paths 会缓存到 ~/Documents/MachOCache 中
# 示例: ➜  Debug sudo ~/Desktop/Test/shell/find_lc_note.sh /usr/sbin/ LC_UUID

# 判断输入文件是否为 Mach-O 文件
# $1: 文件路径
# r: bool


function isMachO() { 
    local machO_heads=("bebafeca" "cafebabe"  # fat_head
            "feedface" "cefaedfe" "feedfacf" "cffaedfe" # mach_header/64
            "72613c21")  # .a "!<ar" 实际应为 "!<arch>\n"
    local head=`od -N4 -tx4 "${1}"`
    for i in ${!machO_heads[@]}; do
        if [[ "${head}" =~ "${machO_heads[${i}]}" ]]; then
            echo true
            return 
        fi
    done
    echo false
}

# 查找指定目录下的所有 Mach-O 文件
# $1: 目录
# r: 文件路径, 换行符分割
function findMachOFiles() {
    local array=()
    # readarray -d "" array < <(find "${1}" -perm +111 -type f -print0)
    while IFS=  read -r -d $'\0'; do
        array+=("$REPLY")
    done < <(find "${1}" -perm +111 -type f -print0)
    
    # TODO: ".a" 文件查找合并为一个 find ?
    while IFS=  read -r -d $'\0'; do
        array+=("$REPLY")
    done < <(find "${1}" -name "*.a" -print0)

    for i in ${!array[@]}
    do
        read -u6
        {
            path="${array[${i}]}"
            if [[ `isMachO "${path}"` == true ]]; then
                echo -ne "${path}\n"
            fi
            echo >&6 # 当进程结束以后，再向FD6中加上一个回车符，即补上了read -u6减去的那个
        } &
    done

    wait # 要有wait，等待所有线程结束
}

# dir 补 "/"
# $1: dir
# r: dir
function dirRepairBackslash() {
    if [[ "${1:(-1):1}" != "/" ]]; then
        echo "${1}/"
    else
        echo "${1}"
    fi
}

# 用 $1 创建缓存 path
# $1: dir
# r: 缓存 path
function createCachePathWithDir() {
    local dir=$(dirRepairBackslash "${1}")
    local filename="${dir//\//__}.txt"
    echo "/Users/marcow/Documents/MachOCache/${filename}"
}

# main--------------------------------------------------------------------------
if [[ "${1}" == "" || "${2}" == ""  ]]; then
    echo "输入参数错误!"
    exit 1
fi

start_time=`date +%s`  #定义脚本运行的开始时间

tmp_fifofile="/tmp/$$.fifo"
mkfifo $tmp_fifofile   # 新建一个FIFO类型的文件
exec 6<>$tmp_fifofile  # 将FD6指向FIFO类型
rm $tmp_fifofile  #删也可以，

thread_num=10  # 定义最大线程数

#根据线程总数量设置令牌个数
#事实上就是在fd6中放置了$thread_num个回车符
for ((i=0;i<${thread_num};i++));do
    echo
done >&6

#-------------------------------------------------------------------------------
cachePath=$(createCachePathWithDir "${1}")
if [ ! -f "${cachePath}" ]; then
    touch "${cachePath}"
    echo "`findMachOFiles "${1}"`" > "${cachePath}" 
fi

while read -r path; do
    read -u6
    {
        lc_note=$(/usr/local/bin/otool-classic -l "${path}" | grep -E "${2}")
        if [[ $lc_note != "" ]]; then
            echo "${2}: ${path}"
        fi
        echo >&6
    } & 
done < "${cachePath}"

wait

stop_time=`date +%s` # 定义脚本运行的结束时间
echo "TIME:`expr $stop_time - $start_time`" # 输出脚本运行时间

exec 6>&- # 关闭FD6
echo "over" # 表示脚本运行结束
